package com.pig4cloud.pig.ads.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.pig4cloud.pig.ads.pig.mapper.AdCreativeComponentMapper;
import com.pig4cloud.pig.ads.pig.mapper.AdvertiserMapper;
import com.pig4cloud.pig.ads.service.AdCreativeComponentService;
import com.pig4cloud.pig.ads.service.TtAccesstokenService;
import com.pig4cloud.pig.ads.utils.OEHttpUtils;
import com.pig4cloud.pig.api.dto.CreativeComponentDto;
import com.pig4cloud.pig.api.entity.AdCreativeComponent;
import com.pig4cloud.pig.api.entity.Advertising;
import com.pig4cloud.pig.api.entity.ResponseBean;
import com.pig4cloud.pig.common.core.constant.CommonConstants;
import com.pig4cloud.pig.common.core.constant.enums.PlatformTypeEnum;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author chengang
 * @Date 2021/8/24
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class AdCreativeComponentServiceImpl extends ServiceImpl<AdCreativeComponentMapper, AdCreativeComponent> implements AdCreativeComponentService {

	private final TtAccesstokenService ttAccesstokenService;
	private final AdvertiserMapper advertiserMapper;

	@Value("${creative_component_create}")
	private String creativeComponentCreate;

	@Value("${creative_component_edit}")
	private String creativeComponentEdit;

	@Value("${creative_component_list}")
	private String creativeComponentList;

	private ThreadPoolExecutor executor;

	//@PostConstruct
	public void initExecute() {
		ThreadFactory threadFactory = new ThreadFactoryBuilder()
				.setNameFormat("creative-job-%d").build();
		//创建线程池 https://blog.csdn.net/sinat_15946141/article/details/107951917
		// corePoolSize 指定了线程池中的线程数量，它的数量决定了添加的任务是开辟新的线程去执行，还是放到workQueue任务队列中去；
		// maximumPoolSize 指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型，决定线程池会开辟的最大线程数量；
		// keepAliveTime 当线程池中空闲线程数量超过corePoolSize时，且线程阻塞队列没有新的线程进来，多余的线程会在多长时间内被销毁
		// unit:keepAliveTime的单位
		// workQueue：用于缓存任务的阻塞队列 暂定200 目前账户91个
		// ThreadFactory 空闲时等待时长
		// CallerRunsPolicy 当线程数=maxPoolSize，且任务队列已满时，由主线程执行
		executor = new ThreadPoolExecutor(2,5,10L,
				TimeUnit.SECONDS,new LinkedBlockingQueue<>(200),threadFactory,new ThreadPoolExecutor.CallerRunsPolicy());
		executor.allowCoreThreadTimeOut(true);
	}

	@Override
	public R component4Create(CreativeComponentDto dto) {
		R check = this.baseCheck(dto);
		if (check.getCode() == CommonConstants.FAIL) {
			return check;
		}
		Integer userId = SecurityUtils.getUser().getId();
		//构建请求参数MAP
		Map<String, Object> paramMap = Maps.newHashMap();
		paramMap.put("advertiser_id",dto.getAdvertiser_id());
		//构建组件信息
		Map<String, Object> componentInfo = Maps.newHashMap();
		componentInfo.put("component_type",StringUtils.isBlank(dto.getComponent_type())?"PROMOTION_CARD":dto.getComponent_type());
		componentInfo.put("component_name",dto.getComponent_name());
		componentInfo.put("component_data",dto.getComponent_data());

		paramMap.put("component_info",componentInfo);

		String accessToken = ttAccesstokenService.fetchAccesstoken(dto.getAdvertiser_id());
		String resultStr = OEHttpUtils.doPost(creativeComponentCreate, paramMap, accessToken);
		log.info("头条创建基础创意组件返回信息:{}",resultStr);
		ResponseBean resBean = JSON.parseObject(resultStr, ResponseBean.class);
		if (resBean != null && "0".equals(resBean.getCode())) {
			JSONObject jsonObj = resBean.getData();
			String componentId = jsonObj.getString("component_id");
			//前端number精度问题，暂时后端强制性转String
			jsonObj.put("component_id",componentId);
			//创建完成需要调用定时器逻辑
			this.getCreativeComponentList(dto.getAdvertiser_id(),componentId);
			return R.ok(jsonObj);
		} else {
			return R.failed(resBean.getMessage());
		}
	}

	public static void main(String[] args) {
		String result = "{\"code\": 0, \"message\": \"OK\", \"request_id\": \"202109141124250102121621424200C38C\", \"data\": {\"modify_time\": \"2021-09-14 11:24:25\", \"component_id\": 7007625113654788110, \"advertiser_id\": 1699519831565326, \"status\": \"UNDER\", \"create_time\": \"2021-09-14 11:24:25\"}}";
		ResponseBean resBean = JSON.parseObject(result, ResponseBean.class);
		JSONObject jsonObj = resBean.getData();
		//String componentId = jsonObj.getString("component_id");
		System.out.println(R.ok( resBean.getData()));
		//System.out.println(componentId);

	}

	@Override
	public R component4Edit(CreativeComponentDto dto) {
		R check = this.baseCheck(dto);
		if (check.getCode() == CommonConstants.FAIL) {
			return check;
		}
		//构建请求参数MAP
		Map<String, Object> paramMap = Maps.newHashMap();
		paramMap.put("advertiser_id",dto.getAdvertiser_id());
		paramMap.put("component_id",dto.getComponent_id());
		//构建组件信息
		Map<String, Object> componentInfo = Maps.newHashMap();
		componentInfo.put("component_type",StringUtils.isBlank(dto.getComponent_type())?"PROMOTION_CARD":dto.getComponent_type());
		componentInfo.put("component_name",dto.getComponent_name());
		componentInfo.put("component_data",dto.getComponent_data());

		paramMap.put("component_info",componentInfo);

		String accessToken = ttAccesstokenService.fetchAccesstoken(dto.getAdvertiser_id());
		String resultStr = OEHttpUtils.doPost(creativeComponentEdit, paramMap, accessToken);
		log.info("头条更新基础创意组件返回信息:{}",resultStr);
		ResponseBean resBean = JSON.parseObject(resultStr, ResponseBean.class);
		if (resBean != null && "0".equals(resBean.getCode())) {
			JSONObject jsonObj = resBean.getData();
			String componentId = jsonObj.getString("component_id");
			//前端number精度问题，暂时后端强制性转String
			jsonObj.put("component_id",componentId);
			//更新完成需要调用定时器逻辑
			this.getCreativeComponentList(dto.getAdvertiser_id(),componentId);
			return R.ok(jsonObj);
		} else {
			return R.failed(resBean.getMessage());
		}
	}

	/**
	 * 获取创意基础组件
	 * @param advertiserIds 执行参数 逗号分割
	 * @param componentId
	 * @return
	 */
	@Override
	public R getCreativeComponentList(String advertiserIds,String componentId) {
		LambdaQueryWrapper<Advertising> query = new LambdaQueryWrapper<>();
		if (StringUtils.isNotBlank(advertiserIds)) {
			List<String> searchAdvertisingList = new ArrayList<>(Arrays.asList(advertiserIds.split(","))).stream().collect(Collectors.toList());
			query.and(wrapper -> wrapper.in(Advertising::getAdvertiserId,searchAdvertisingList));
		}
		query.and(wrapper -> wrapper.eq(Advertising::getDeleted, 0));
		query.and(wrapper -> wrapper.eq(Advertising::getPlatformId, PlatformTypeEnum.TT.getValue()));
		//获取平台所有的广告账户
		List<Advertising> advertisingList = advertiserMapper.selectList(query);
		if (CollectionUtils.isNotEmpty(advertisingList)) {
			this.initExecute();
			advertisingList.forEach(advertising -> {
				//获取accessToken
				String accessToken = ttAccesstokenService.fetchAccesstoken(advertising.getAdvertiserId());
				executor.submit(new GetCreativeComponentJob(this,advertising.getAdvertiserId(),accessToken,componentId));
			});
		} else {
			return R.ok("没有获取到广告账户");
		}

		executor.shutdown();

		return R.ok("获取创意基础组件完成");
	}

	class GetCreativeComponentJob implements Runnable {
		private AdCreativeComponentService adCreativeComponentService;
		private String advertiserId;
		private String accessToken;
		private String componentId;

		private GetCreativeComponentJob(AdCreativeComponentService adCreativeComponentService,String advertiserId,String accessToken,String componentId) {
			this.adCreativeComponentService = adCreativeComponentService;
			this.advertiserId = advertiserId;
			this.accessToken = accessToken;
			this.componentId = componentId;
		}

		@Override
		public void run() {
			try {
				//新增或编辑的情况，立马查询会没有值
				if (StringUtils.isNotBlank(componentId)) {
					TimeUnit.SECONDS.sleep(3);
				}
				int page = 1;
				while (true) {

					//构建请求参数MAP
					Map<String, Object> paramMap = Maps.newHashMap();
					paramMap.put("advertiser_id",advertiserId);
					paramMap.put("page",page);
					paramMap.put("page_size",20);

					//构建过滤条件参数MAP
					Map<String, Object> filteringMap = Maps.newHashMap();
					//目前仅获取推广卡片类型的基础创意组件
					filteringMap.put("component_types",new String[]{"PROMOTION_CARD"});
					if (StringUtils.isNotBlank(componentId)) {
						filteringMap.put("component_id",componentId);
					}
					paramMap.put("filtering",filteringMap);

					String resultStr = OEHttpUtils.doGet(creativeComponentList, paramMap, accessToken);
					ResponseBean resBean = JSON.parseObject(resultStr, ResponseBean.class);
					if (resBean != null && "0".equals(resBean.getCode())) {
						JSONObject jsonObj = resBean.getData();
						String listStr = jsonObj.getString("list");
						String page_info = jsonObj.getString("page_info");
						// String[] 类型的会转 /creative/temp/create化为JSON类型的String
						List<AdCreativeComponent> list = JSONObject.parseArray(listStr, AdCreativeComponent.class);
						//存储 ad_creative_component
						List<AdCreativeComponent> insertList = Lists.newArrayList();
						list.forEach(component ->{
							component.setAdvertiserId(advertiserId);
							// -1 表示定时器更新
							component.setCreateId(-1L);
							component.setUpdateId(-1L);
							AdCreativeComponent adCreativeComponent = adCreativeComponentService.getOne(Wrappers.<AdCreativeComponent>lambdaQuery()
									.eq(AdCreativeComponent::getComponentId,component.getComponentId())
									.eq(AdCreativeComponent::getAdvertiserId,advertiserId));
							if (adCreativeComponent != null) {
								//有修改则更新 状态变更-创建时间不会变且没有修改时间
//								if (!adCreativeComponent.getThirdCreateTime().equals(component.getThirdCreateTime())) {
									adCreativeComponentService.update(component,Wrappers.<AdCreativeComponent>lambdaQuery()
											.eq(AdCreativeComponent::getComponentId,component.getComponentId())
											.eq(AdCreativeComponent::getAdvertiserId,advertiserId));
//								}
							} else {
								insertList.add(component);
							}
						});
						if (CollectionUtils.isNotEmpty(insertList)) {
							adCreativeComponentService.saveBatch(insertList);
						}

						JSONObject jsonObject = JSONObject.parseObject(page_info);
						Long total_page = jsonObject.getLong("total_page");
						if (page >= total_page) {
							break;
						}
						page++;
					} else {
						log.info("获取基础创意组件异常：{},广告账户：{}",resBean.getMessage(),advertiserId);
						break;
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
				log.info("获取基础创意组件异常,广告账户：{}",advertiserId);
			}
		}
	}

	private R baseCheck(CreativeComponentDto dto){
		JSONObject data = JSON.parseObject(dto.getComponent_data());
		String imageId = data.getString("image_id");
		String title = data.getString("title");
		String buttonText = data.getString("button_text");
		int enablePersonalAction = data.getIntValue("enable_personal_action");
		JSONArray productSellingPoints = data.getJSONArray("product_selling_points");
		if (StringUtils.isBlank(imageId)) {
			return R.failed("卡片主图不能为空");
		}
		if (StringUtils.isBlank(title)) {
			return R.failed("卡片标题不能为空");
		}
		if (productSellingPoints == null || productSellingPoints.size()<=0) {
			return R.failed("推广卖点不能为空");
		}
		if (StringUtils.isBlank(buttonText)) {
			return R.failed("行动号召不能为空");
		}
		if (title.length() > 7) {
			return R.failed("卡片标题长度最大为7");
		}
		if (buttonText.length() < 2 || buttonText.length()>6) {
			return R.failed("行动号召长度范围为[2,6]");
		}
		if (productSellingPoints.size()>10) {
			return R.failed("最多选择10个推广卖点");
		}
		String errorMsg = "";
		List<String> existPoints = Lists.newArrayList();
		for (int i=0;i<productSellingPoints.size();i++) {
			String points = productSellingPoints.getString(i);
			if (points.length() < 6 || points.length()>9) {
				errorMsg = "推广卖点长度范围为[6,9]";
				break;
			}
			if (existPoints.contains(points)) {
				errorMsg = "推广卖点不能重复";
				break;
			} else {
				existPoints.add(points);
			}
		}
		if (StringUtils.isNotBlank(errorMsg)) {
			return R.failed(errorMsg);
		}
		return R.ok();
	}
}
